import java.util.ArrayList;


public class Minimax {
	private Node givenState;
	private ArrayList<Node> children;
	private GameBoardTool gTool = new GameBoardTool();
	private static int MIN;
	private static int MAX;
	public int recursions = 0;
	private int border;

	public Minimax(GameBoard gm, int player) {
		children = new ArrayList<Node>();
		givenState = new Node(gm, children);
		border = givenState.getGameBoard().numberOfMoves(); 
		MAX = player;
		if (MAX == 1) MIN = 2;
		else MIN = 1;
	}

	public int getAction(){
		int action = -1;
		//get the utility of each action possible for max
		procreate(givenState, MAX);

		float maxUtility = 0;
		int index = 1;
		//Choose the action with the highest value
		for (Node treeNode : givenState.getChildren()) {
			if(!treeNode.isDead() ){
				treeNode.setUtility(minimize(treeNode, index, -10, 10).getUtility());
				if(treeNode.getUtility() > maxUtility || maxUtility == 0){
					maxUtility = treeNode.getUtility();
					//get the index of the action, i.e. the column to play.
					action = index;
				}
			}
			index++;
		}
		return (action - 1);
	}

	private void procreate(Node fromState, int player){
		for (int i = 1; i <= fromState.getGameBoard().getNumColumns(); i++){
			GameBoard tmp = fromState.getGameBoard().peakColumn(i, player);
			if (tmp == null){
				Node child = new Node(tmp, null);
				child.setDead(true);
				fromState.setChild(child);
			}else{//if the column is full create a child with utility null
				ArrayList<Node> grandChildren = new ArrayList<Node>();
				Node child = new Node(tmp, grandChildren);
				fromState.setChild(child);
			}
		}
	}
	
	private Result minimize(Node state, int column, float alpha, float beta){
		Result result = null;
		recursions++;
		//if the column given as argument to the method is full or if we reach a depth of 7
		if(gTool.columnFull(state.getGameBoard() ,column) || state.getGameBoard().numberOfMoves() == border + 9|| gTool.isWinningColumn(state.getGameBoard(), column, MAX)){
			//return the value of the move
			return new Result(gTool.columnValue(state.getGameBoard(), column, MAX, 1), alpha, beta);
		}	//otherwise continue the recursion to the next move
		procreate(state, MIN);
		int col = 1;
		for (Node n : state.getChildren()) {
			if(!n.isDead()){
				result = maximize(n,col,alpha,beta);
				
				n.setUtility(result.getUtility());
				if(result.getUtility() <= alpha){
//				if(result.getUtility() <= result.getAlpha()){
					return result;
				}
				result.setBeta(min(result.getBeta(), result.getUtility()));
//				beta = min(beta, result.getUtility());
			}
			col++;
		}
		return result;
	}
	
	private Result maximize(Node state, int column, float alpha, float beta){
		Result result = null;
		recursions++;
		//if the column given as argument to the method is full or if we reach a depth of 7
		if(gTool.columnFull(state.getGameBoard() ,column) || state.getGameBoard().numberOfMoves() == border + 9 || gTool.isWinningColumn(state.getGameBoard(), column, MIN)){
			//return the value of the move
			return new Result(gTool.columnValue(state.getGameBoard(), column, MIN, -1), alpha, beta);
		}	//otherwise continue the recursion to the next move
		procreate(state, MAX);
		int col = 1;
		for (Node n : state.getChildren()) {
			if(!n.isDead()){
				result = minimize(n,col,alpha,beta);//new Result(n.getUtility(), alpha, beta);
				n.setUtility(result.getUtility());
				if(result.getUtility() >= beta){
//				if(result.getUtility() >= result.getBeta()){
					return result;
				}
				result.setAlpha(max(result.getAlpha(), result.getUtility()));
				alpha = max(alpha, result.getUtility());
			}
			col++;
		}
		return result;
	}
	
	private float min(float a, float b){
		if (a < b) return a;
		else return b;
	}
	
	private float max(float a, float b){
		if (a > b) return a;
		else return b;
	}
}
